package net.javastone.toolkit.core.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.experimental.UtilityClass;
import net.javastone.toolkit.core.dataformat.BitSetDeserializer;
import net.javastone.toolkit.core.dataformat.BitSetSerializer;
import net.javastone.toolkit.core.dataformat.DataFormatTransfer;

import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * JSON数据处理类
 */
@UtilityClass
public final class Jsons {

  public static final ObjectMapper objectMapper;

  static {
    objectMapper = new ObjectMapper();
    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);
    objectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    SimpleModule module = new SimpleModule(objectMapper.getClass().getSimpleName(),
        new Version(1, 0, 0, null, null, null));
    module.addSerializer(new BitSetSerializer());
    module.addDeserializer(BitSet.class, new BitSetDeserializer());
    objectMapper.registerModule(module);
  }

  public static ObjectReader reader(Class<?> type) {
    return objectMapper.readerFor(type);
  }

  public static ObjectReader reader(TypeReference<?> type) {
    return objectMapper.readerFor(type);
  }

  public static ObjectReader reader(JavaType type) {
    return objectMapper.readerFor(type);
  }

  public static ObjectWriter writer() {
    return objectMapper.writer();
  }

  public static String string(Object object) {
    return of(object).toString();
  }

  public static JsonNode tree(Object object) {
    return of(object).toTreeOrNullNode();
  }

  public static DataFormatTransfer of(Object source) {
    return new DataFormatTransfer(source, objectMapper);
  }

  /**
   * 找出所有key
   *
   * @param jsonNode json串
   * @return
   */
  public static Set<String> keys(JsonNode jsonNode) {
    return keys(jsonNode, "");
  }

  /**
   * 扁平化
   *
   * @param jsonNode
   * @return
   */
  public static Map<String, String> flat(JsonNode jsonNode) {
    return flat(jsonNode, "");
  }

  /**
   * 找出所有key
   *
   * @param jsonNode  json节点
   * @param parentKey 父节点
   * @return
   */
  private static Set<String> keys(JsonNode jsonNode, String parentKey) {
    Set<String> keys = new HashSet<>();
    //是否集合
    if (jsonNode.isArray()) {
      final String finalParentKey = parentKey;
      jsonNode.forEach(child -> {
        if (child != null && !child.isNull()) {
          keys.addAll(keys(child, finalParentKey));
        }
      });
    } else {
      //parentKey处理
      if (parentKey != null && !"".equals(parentKey)) {
        parentKey += ".";
      }
      //遍历属性
      String fieldName;
      JsonNode fieldValue;
      Iterator<String> fieldNames = jsonNode.fieldNames();
      while (fieldNames.hasNext()) {
        fieldName = fieldNames.next();
        keys.add(parentKey + fieldName);
        fieldValue = jsonNode.get(fieldName);
        //属性值递归
        if (fieldValue.isContainerNode()) {
          keys.addAll(keys(fieldValue, parentKey + fieldName));
        }
      }
    }
    return keys;
  }

  /**
   * 扁平化
   *
   * @param jsonNode
   * @return
   */
  private static Map<String, String> flat(JsonNode jsonNode, String parentKey) {
    Map<String, String> map = new HashMap<>();
    //是否集合
    if (jsonNode.isArray()) {
      final String finalParentKey = parentKey;
      jsonNode.forEach(child -> {
        if (child != null && !child.isNull()) {
          map.putAll(flat(child, finalParentKey));
        }
      });
    } else {
      //parentKey处理
      if (parentKey != null && !"".equals(parentKey)) {
        parentKey += ".";
      }
      //遍历属性
      String fieldName;
      JsonNode fieldValue;
      Iterator<String> fieldNames = jsonNode.fieldNames();
      while (fieldNames.hasNext()) {
        fieldName = fieldNames.next();
        fieldValue = jsonNode.get(fieldName);
        //属性值递归
        if (fieldValue.isContainerNode()) {
          map.putAll(flat(fieldValue, parentKey + fieldName));
        } else {
          map.put(parentKey + fieldName, fieldValue.asText());
        }
      }
    }
    return map;
  }
}
